package Common 
{
	import Box2D.Collision.b2AABB;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2FixtureDef;
	import Box2D.Dynamics.b2World;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	/**
	 * ...
	 * @author Yevgeniy Logachev
	 */
	public class GamePhysics 
	{
		private static var m_physicalWorldAABB:b2AABB 		= null;
		private static var m_physicalWorld:b2World			= null;
		private static const m_worldScale:Number 			= 30;
		private static const m_stepLimiter:Number 			= 1.0 / 30.0
		
		private static const s_instance:GamePhysics 		= new GamePhysics();
		private var m_debugDraw:b2DebugDraw;
		private var m_enableDebug:Boolean					= false;
		private var m_debugSprite:Sprite;
		
		public function GamePhysics() 
		{
			if (s_instance == null)
			{
				createPhysicalWorld();
				trace ("GamePhysics initialized");
			}
			else
			{
				trace("ERROR: Already existing instance in Singleton");
				throw new Error("Already existing instance in Singleton");
			}
		}
		
		public static function get instance():GamePhysics
		{
			return s_instance;
		}
		
		public static function createPhysicalBody(posX:int, posY:int, startRot:int, dispObject:DisplayObject, isStatic:Boolean = false, shapeScale:Number = 1.0, linearDamping:Number = 2.0):b2Body
		{
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.position.Set(posX / GamePhysics.instance.physicalScale, posY / GamePhysics.instance.physicalScale);
			//bodyDef.angle = startRot * Math.PI / 180.0;
			if (isStatic)
			{
				bodyDef.type = b2Body.b2_staticBody;
			}
			else
			{
				bodyDef.type = b2Body.b2_dynamicBody;
			}
			bodyDef.linearDamping = linearDamping;
			
			dispObject.rotation = 0;
			var shape:b2PolygonShape = new b2PolygonShape();
			shape.SetAsBox((dispObject.width / 2) / GamePhysics.instance.physicalScale * shapeScale, (dispObject.height / 2) / GamePhysics.instance.physicalScale * shapeScale);
			
			var fixture:b2FixtureDef = new b2FixtureDef();
			fixture.shape = shape;
			
			var physicalBody:b2Body = GamePhysics.instance.physicalWorld.CreateBody(bodyDef);
			physicalBody.CreateFixture(fixture);
			physicalBody.SetUserData(dispObject);
			physicalBody.SetAngle(startRot * (Math.PI / 180.0));
			
			return physicalBody;
		}
		
		public function createPhysicalWorld():void
		{
			// Create phisical world
			m_physicalWorldAABB = new b2AABB();
			m_physicalWorldAABB.lowerBound.Set(-1000.0, -1000.0);
			m_physicalWorldAABB.upperBound.Set(1000.0, 1000.0);
			// Construct a world object
			m_physicalWorld = new b2World(new b2Vec2(0.0, 0.0), true);
			m_physicalWorld.SetWarmStarting(true);
		}
		
		// Draw regions for physicals objects
		public function debugDraw(scale:Number = 1.0):void 
		{
			m_debugDraw = new b2DebugDraw();
			m_debugSprite = new Sprite();
			Main.mainSprite.addChild(m_debugSprite);
			m_debugDraw.SetSprite(m_debugSprite);
			m_debugDraw.SetDrawScale(m_worldScale);
			m_debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
			m_debugDraw.SetDrawScale(scale * m_worldScale);
			m_debugSprite.alpha = 0.5;
			m_physicalWorld.SetDebugDraw(m_debugDraw);
		}
		
		public function get enableDebug():Boolean
		{
			return m_enableDebug;
		}
		
		public function set enableDebug(value:Boolean):void
		{
			m_enableDebug = value;
			if (!m_enableDebug)
			{
				m_debugSprite.graphics.clear();
			}
		}
		
		public function get physicalWorld():b2World
		{
			if (m_physicalWorld == null)
			{
				createPhysicalWorld();
			}
			return m_physicalWorld;
		}
		
		public function setDebugScale(value:Number):void
		{
			m_debugDraw.SetDrawScale(value * m_worldScale);
		}
		
		public function get physicalScale():Number
		{
			return m_worldScale;
		}
		
		public static function get stepLimiter():Number
		{
			return m_stepLimiter;
		}
	}

}